<?php

namespace W3;

use Manager\Attach as Manager;
use function add_action;

!defined('W3_ROOT_DIR') AND exit;

/**
 * 上传组件
 *
 * @author edikud
 * @date 2022/10/22
 * @copyright Copyright (c) 2022 W3 (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
class Uploader extends Manager
{
	
    # 上传文件目录 (物理路径，可以用 NFS 存入到单独的文件服务器)
    public const UPLOAD_DIR = 'app/uploads';
	
    /**
     * 初始化
     *
     * @access protected
     * @return void
     */
    protected function init()
    {
		# 定义变量默认数据
        $this->parameter([

			# 设置主体
			'main' => 'uploader'
			
		], true);
		
		/** 插件接口 实现自己的文件删除处理函数 */
        add_action('uploader@delete', [Uploader::class, '_deleteHandle']);
		
		/** 插件接口 实现自己的文件哈希或者特殊的文件系统 */
        add_action('uploader@uploadHandle', [Uploader::class, '_uploadHandle']);
	}
	
    /**
     * 上传文件处理函数
     *
     * @access public
     * @param array $file 上传的文件
     * @return mixed
     */
    public static function _uploadHandle($widget, $file)
    {
        if (empty($file['name'])) {
            return false;
        }

        $ext = self::getSafeExt($file['name']);
		
		# 检查文件文件类型
        if (!in_array($ext, $widget->allowedFileTypes())) {
            return false;
        }

		//文件路径
        $date = Timer::make();
        $path = W3_ROOT_DIR . self::UPLOAD_DIR . '/' . $date->year . '/' . $date->month;

        //创建上传目录
        if (!is_dir($path)) {
            if (!self::makeUploadDir($path)) {
                return false;
            }
        }

        //获取文件名
        $fileName = sprintf('%u', crc32(uniqid())) . '.' . $ext;
        $path = $path . '/' . $fileName;

        if (isset($file['tmp_name'])) {

            //移动上传文件
            if (!@move_uploaded_file($file['tmp_name'], $path)) {
                return false;
            }
        } else if (isset($file['bytes'])) {

            //直接写入文件
            if (!file_put_contents($path, $file['bytes'])) {
                return false;
            }
        } else {
            return false;
        }

        if (!isset($file['size'])) {
            $file['size'] = filesize($path);
        }

        //返回相对存储路径
        return array(
            'name' => $file['name'],
            'path' => '/' . $date->year . '/' . $date->month . '/' . $fileName,
            'size' => $file['size'],
            'ext'  => $ext,
            'mime' => Filer::mime($path)
        );
    }
	
    /**
     * 删除文件
     *
     * @param array $info 文件相关信息
     * @return bool
     */
    public static function _deleteHandle($widget, array $info): bool
    {
        return @unlink(W3_ROOT_DIR . self::UPLOAD_DIR . '/' . ltrim($info['path'], '/'));
    }
	
    /**
     * 创建上传路径
     *
     * @param string $dir 路径
     * @return boolean
     */
    private static function makeUploadDir(string $dir, int $chmod = 0777): bool
    {
        return is_dir($dir) || self::makeUploadDir(dirname($dir), $chmod) && mkdir($dir, $chmod);
    }

    /**
     * 获取安全的文件名 
     * 
     * @param string $name 
     * @static
     * @access private
     * @return string
     */
    private static function getSafeExt(&$name)
    {
        $name = str_replace(array('"', '<', '>'), '', $name);
        $name = str_replace('\\', '/', $name);
        $name = false === strpos($name, '/') ? ('a' . $name) : str_replace('/', '/a', $name);
        $info = pathinfo($name);
        $name = substr($info['basename'], 1);
    
        return isset($info['extension']) ? strtolower($info['extension']) : '';
    }

    /**
     * 执行升级程序
     *
     * @access public
     * @return void
     */
    public function upload($type, $id)
    {
		if (empty($_FILES)) {
		    return false;
		}
		
        $file = array_pop($_FILES);
        if (0 != $file['error'] || !is_uploaded_file($file['tmp_name'])) {
		    return false;
		}
		
        // xhr的send无法支持utf8
        if ($this->request->isAjax()) {
            $file['name'] = urldecode($file['name']);
        }		

        $result = $this->uploadHandle($file);

        if (false === $result) {
			return false;
		}	

        $values = [
            'id'             =>  $id,
            'type'           =>  $type,
            'name'           =>  $result['name'],
            'ext'            =>  $result['ext'],
            'size'           =>  $result['size'],
            'path'           =>  $result['path'],
            'created'        =>  $this->config->time,
            'isImage'        =>  in_array($result['ext'], ['jpg', 'jpeg', 'gif', 'png', 'tiff', 'bmp']),
            'uid'            =>  $this->auth->uid,
        ];
				
        $insertId = $this->addAttach($values);
        $attach = $this->db
			->select()
			->from('table.attachs')
			->where('table.attachs.aid = ?', $insertId)
            ->fetch([$this, 'push']);

        /** 插件接口 */
		do_action('upload', $this);

        return $attach;
    }

    /**
     * 绑定动作
     *
     * @access public
     * @return void
     */
    public function action()
    {
		$this->request->data('isAjax', true);
		$type    = $this->request->filter('strip_tags')->type;
		$id      = $this->request->filter('int')->id;
		$attach  = $this->upload($type, $id);

		$this->throwJson(!empty($attach) ? $attach : false);
	}
}
